<?php
namespace Sfsn\Core\Http;

/**
 * http请求类
 *
 * 接收http get post put等请求数据
 *
 * @author shooke
 */
class Request
{

    const METHOD_HEAD = 'HEAD';

    const METHOD_GET = 'GET';

    const METHOD_POST = 'POST';

    const METHOD_PUT = 'PUT';

    const METHOD_PATCH = 'PATCH';

    const METHOD_DELETE = 'DELETE';

    const METHOD_OPTIONS = 'OPTIONS';

    const METHOD_OVERRIDE = '_METHOD';

    /**
     * 路由请求
     *
     * 依赖Route类对该变量进行赋值
     *
     * @var \Sfsn\Core\Http\Request
     *
     */
    public static $route;

    /**
     * 设置route数据
     * 
     * @param array $route            
     */
    public static function setRoute($route)
    {
        self::$route = $route;
    }

    /**
     * 获取请求类型
     *
     * @return string
     */
    public static function getMethod()
    {
        return strtoupper($_SERVER['REQUEST_METHOD']);
    }

    /**
     * 判断是否get请求
     *
     * @return bool
     */
    public static function isGet()
    {
        return self::getMethod() === self::METHOD_GET;
    }

    /**
     * 判断是否post请求
     *
     * @return bool
     */
    public static function isPost()
    {
        return self::getMethod() === self::METHOD_POST;
    }

    /**
     * 判断是否put请求
     *
     * @return bool
     */
    public static function isPut()
    {
        return self::getMethod() === self::METHOD_PUT;
    }

    /**
     * 判断是否patch请求
     *
     * @return bool
     */
    public static function isPatch()
    {
        return self::getMethod() === self::METHOD_PATCH;
    }

    /**
     * 判断是否delete请求
     *
     * @return bool
     */
    public static function isDelete()
    {
        return self::getMethod() === self::METHOD_DELETE;
    }

    /**
     * 判断是否head请求
     *
     * @return bool
     */
    public static function isHead()
    {
        return self::getMethod() === self::METHOD_HEAD;
    }

    /**
     * 判断是否options请求
     *
     * @return bool
     */
    public static function isOptions()
    {
        return self::getMethod() === self::METHOD_OPTIONS;
    }

    /**
     * 判断是否ajax请求
     *
     * @return bool
     */
    public static function isAjax()
    {
        if (self::param('isajax')) {
            return true;
        } elseif (self::server('X_REQUESTED_WITH') === 'XMLHttpRequest') {
            return true;
        }
        
        return false;
    }

    /**
     * 判断是否ajax请求，isAjax的别名
     *
     * @return bool
     */
    public static function isXhr()
    {
        return self::isAjax();
    }

    /**
     * 请求是否使用的是HTTPS安全链接
     *
     * 如果是安全请求则返回true否则返回false
     *
     * @return boolean
     */
    public static function isSecure()
    {
        return ! strcasecmp(self::server('HTTPS'), 'on');
    }

    /**
     * 获取get和post两种请求的数据合集
     *
     * 如果key重名post会覆盖get
     *
     * @param string $key            
     * @param mixed $default            
     * @return array|mixed|null
     */
    public static function param($key = null, $default = null)
    {
        // 方法中用到的变量
        $output = null;
        $get = self::get() ? self::get() : [];
        $post = self::post() ? self::post() : [];
        $param = self::$route['param'] ? self::$route['param'] : [];
        $param = array_merge($param, $get, $post);
        // 处理返回数据
        if (is_null($key)) {
            $output = empty($param) ? $default : $param;
        } else {
            $output = isset($param[$key]) ? $param[$key] : $default;
        }
        
        // 由于数据在get和post处理过所以直接返回
        return $output;
    }

    /**
     * 获取$_REQUEST请求数据
     *
     * 这是get post cookie合集数据
     *
     * @param string $key            
     * @param string $default            
     * @return Ambigous <multitype:array string , array>
     */
    public static function request($key = null, $default = null)
    {
        // 方法中用到的变量
        $output = null;
        $request = null;
        
        // 取得数据
        $request = $_REQUEST;
        
        // 处理返回数据
        if (is_null($key)) {
            $output = empty($request) ? $default : $request;
        } else {
            $output = isset($request[$key]) ? $request[$key] : $default;
        }
        
        // 做魔术引号处理
        return self::magicQuotes($output);
    }

    /**
     * 获取get请求的数据
     *
     * @param string $key            
     * @param string $default            
     * @return Ambigous <multitype:array string , array>
     */
    public static function get($key = null, $default = null)
    {
        // 方法中用到的变量
        $output = null;
        $get = null;
        
        // 取得数据
        if ($_GET) {
            $get = $_GET;
        } else {
            // 从$_SERVER['QUERY_STRING']中获取数据
            $rawInput = self::server('QUERY_STRING');
            // 进行数组处理
            if (function_exists('mb_parse_str')) {
                mb_parse_str($rawInput, $get);
            } else {
                parse_str($rawInput, $get);
            }
        }
        
        // 处理返回数据
        if (is_null($key)) {
            $output = empty($get) ? $default : $get;
        } else {
            $output = isset($get[$key]) ? $get[$key] : $default;
        }
        
        // 做魔术引号处理
        return self::magicQuotes($output);
    }

    /**
     * 获取post请求的数据
     *
     * @param string $key            
     * @param string $default            
     * @return Ambigous <multitype:array string , array>
     */
    public static function post($key = null, $default = null)
    {
        // 方法中用到的变量
        $output = null;
        $post = null;
        /*
         * 如果有正常的post数据则处理返回如果没有则返回input进来的数据
         */
        if ($_POST) {
            $post = $_POST; // 做魔术引号处理
        } else {
            // 从PHP://input中获取数据
            $rawInput = self::input();
            // 进行数组处理
            if (function_exists('mb_parse_str')) {
                mb_parse_str($rawInput, $post);
            } else {
                parse_str($rawInput, $post);
            }
        }
        
        // 处理返回数据
        if (is_null($key)) {
            $output = empty($post) ? $default : $post;
        } else {
            $output = isset($post[$key]) ? $post[$key] : $default;
        }
        
        // 做魔术引号处理
        return self::magicQuotes($output);
    }

    /**
     * 获取put请求的数据
     *
     * @param string $key            
     * @param string $default            
     * @return \Sfsn\Core\Http\Ambigous
     */
    public static function put($key = null, $default = null)
    {
        return self::post($key, $default);
    }

    /**
     * 获取patch请求的数据
     *
     * @param string $key            
     * @param string $default            
     * @return \Sfsn\Core\Http\Ambigous
     */
    public static function patch($key = null, $default = null)
    {
        return self::post($key, $default);
    }

    /**
     * 获取delete请求的数据
     *
     * @param string $key            
     * @param string $default            
     * @return \Sfsn\Core\Http\Ambigous
     */
    public static function delete($key = null, $default = null)
    {
        return self::post($key, $default);
    }

    /**
     * 返回Server的值
     *
     * 如果$name为空则返回所有Server的值
     *
     * @param string $name
     *            获取的变量名,如果该值为null则返回$_SERVER数组,默认为null
     * @param string $default
     *            当获取变量失败的时候返回该值,默认该值为null
     * @return mixed
     */
    public static function server($key = null, $default = null)
    {
        $output = null;
        if (is_null($key)) {
            $output = $_SERVER;
        } else {
            $output = isset($_SERVER[$key]) ? $_SERVER[$key] : $default;
        }
        return $output;
    }

    /**
     * 返回路由解析的值
     *
     * 如果$name为空则返回所有Server的值
     *
     * @param string $key
     *            获取的变量名,如果该值为null则返回$_SERVER数组,默认为null
     * @param string $default
     *            当获取变量失败的时候返回该值,默认该值为null
     * @return mixed
     */
    public static function route($key = null, $default = null)
    {
        // 方法中用到的变量
        $output = null;
        $route = self::$route;
        
        // 处理返回数据
        if (is_null($key)) {
            $output = empty($route) ? $default : $route;
        } else {
            $output = isset($route[$key]) ? $route[$key] : $default;
        }
        
        // 做魔术引号处理
        return self::magicQuotes($output);
    }

    /**
     * 获取php://input
     *
     * 数据用于post put等形式数据
     *
     * @return unknown
     */
    public static function input()
    {
        $output = @file_get_contents('php://input');
        return $output;
    }

    /**
     * 获取客户端ip
     *
     * @param string $default
     *            默认ip，当无法获取客户端ip时使用
     * @return Ambigous <string, unknown>
     */
    public static function getClientIp($default = '0.0.0.0')
    {
        if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
            $arr = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
            $pos = array_search('unknown', $arr);
            if (false !== $pos)
                unset($arr[$pos]);
            $ip = trim($arr[0]);
        } elseif (isset($_SERVER['HTTP_CLIENT_IP'])) {
            $ip = $_SERVER['HTTP_CLIENT_IP'];
        } elseif (isset($_SERVER['REMOTE_ADDR'])) {
            $ip = $_SERVER['REMOTE_ADDR'];
        }
        // IP地址合法验证
        $ip = (false !== ip2long($ip)) ? $ip : $default;
        return $ip;
    }

    /**
     * 获取请求链接协议
     *
     * 如果是安全链接请求则返回https否则返回http
     * 
     * @return string
     */
    public static function getScheme()
    {
        return self::isSecure() ? 'https' : 'http';
    }

    /**
     * 获得主机信息，包含协议信息，主机名，访问端口信息
     *
     * <pre>Example:
     * 请求: http://www.example.net/example/index.php?a=test
     * 返回： www.example.net
     * </pre>
     * 
     * @throws Exception 获取主机信息失败的时候抛出异常
     */
    public static function getHost()
    {
        $host = null;
        
        if (self::server('HTTP_HOST') != null)
            $host = self::server('HTTP_HOST');
        elseif (self::server('SERVER_NAME') != null) {
            $host = self::server('SERVER_NAME');
        }
        
        return $host;
    }

    /**
     * 获得主机端口
     *
     * @return int
     */
    public static function getPort()
    {
        return (int) self::server('SERVER_PORT');
    }

    /**
     * 获得主机信息，包含协议信息，主机名，访问端口信息
     *
     * <pre>Example:
     * 请求: http://www.example.net:8080/example/index.php?a=test
     * 返回： http://www.example.net:8080
     * </pre>
     * 
     * @throws Exception 获取主机信息失败的时候抛出异常
     */
    public static function getHostWithPort()
    {
        return sprintf('%s://%s:%s', self::getScheme(), self::getHost(), self::getPort());
    }

    /**
     * 获得执行文件
     *
     * <pre>Example:
     * 请求: http://www.example.net:8080/example/index.php?a=test
     * 返回： /example/index.php
     * </pre>
     * 
     * @return string
     */
    public static function getScriptName()
    {
        return self::server('SCRIPT_NAME');
    }

    /**
     * 获得请求uri
     *
     * <pre>Example:
     * 请求: http://www.example.net:8080/example/index.php?a=test
     * 返回： /example/index.php?a=test
     * </pre>
     * 
     * @return string
     */
    public static function getUri()
    {
        return self::server('REQUEST_URI');
    }

    /**
     * 获得请求path_info
     *
     * <pre>Example:
     * 请求: http://www.example.net:8080/example/index.php/group/?a=test
     * 返回： /group/
     * </pre>
     * 
     * @return string
     */
    public static function getPathInfo()
    {
        $pathInfo = null;
        
        if (self::server('PATH_INFO')) {
            $pathInfo = self::server('PATH_INFO');
        } else {
            $pathInfo = str_replace(self::server('SCRIPT_NAME'), '', self::server('PHP_SELF'));
        }
        
        return $pathInfo;
    }

    /**
     * 获取基础URL
     *
     * 这里是去除了脚本文件以及访问参数信息的URL地址信息,如果端口不是80则带有端口号
     *
     * <pre>Example:
     * 请求: http://www.example.net/example/index.php?a=test
     * 1]如果: $absolute = false：
     * 返回： /example
     * 2]如果: $absolute = true:
     * 返回： http://www.example.net/example
     * http://www.example.net:8080/example
     * </pre>
     * 
     * @param boolean $absolute
     *            是否返回主机信息
     * @return string
     */
    public static function getBaseUrl($absolute = false)
    {
        $dir = str_replace('\\', '', dirname(self::getScriptName())); // 目录
        $port = self::getPort() == 80 ? '' : ':' . self::getPort(); // 端口
        return $absolute ? self::getScheme() . '://' . self::getHost() . $port . $dir : $dir;
    }

    /**
     * 获取完整URL
     *
     * 这里是去除了脚本文件以及访问参数信息的URL地址信息,如果端口不是80则带有端口号
     *
     * <pre>Example:
     * 请求: http://www.example.net/example/index.php?a=test
     * 返回： http://www.example.net/example/index.php?a=test
     * </pre>
     * 
     * @return string
     */
    public static function getFullUrl()
    {
        $port = self::getPort() == 80 ? '' : ':' . self::getPort(); // 端口
        return self::getScheme() . '://' . self::getHost() . $port . self::getUri();
    }

    /**
     * 获取 User Agent
     *
     * @return string|null
     */
    public function getUserAgent()
    {
        return self::server('HTTP_USER_AGENT');
    }

    /**
     * : 根据条件处理魔术引号
     * 如果设置了$overrideStripSlashes则根据设置处理$rawData如果未设置则根据默认设置处理
     *
     * @author : Administrator
     * @param array|string $rawData            
     * @param bool $overrideStripSlashes            
     */
    protected static function magicQuotes($rawData, $overrideStripSlashes = null)
    {
        $strip = is_null($overrideStripSlashes) ? get_magic_quotes_gpc() : $overrideStripSlashes;
        if ($strip) {
            return self::stripSlashes($rawData);
        }
        
        return $rawData;
    }

    /**
     * : 递归调用魔术引号处理
     *
     * @author : shooke
     * @param array|strgng $rawData            
     */
    protected static function stripSlashes($rawData)
    {
        return is_array($rawData) ? array_map([
            'self',
            'stripSlashes'
        ], $rawData) : stripslashes($rawData);
    }

    /**
     * 当输出本类对象时显示类名
     * 
     * @return string
     */
    public function __toString()
    {
        return __CLASS__;
    }
}

